﻿using UnityEngine;
using System.Collections;
using UnityEditor;
using System;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using ProfilerDataExporter;
using System.Linq;
public class ShowRenderVertexCount : EditorWindow
{
    public const string CONSTANT_NAME = "---";



    #region MeshRef
    Vector2 curScrollPos;
    List<MeshRefInfo> meshRefList = new List<MeshRefInfo>();
    MeshRefTableState functionStatsTableState;
    string[][] functionStats;

    public class MeshRefInfo
    {
        public Mesh mesh;
        public int refCount;
        public int totalVertex
        {
            get
            {
                int ret = 0;
                if (mesh != null)
                {
                    ret = mesh.vertexCount * refCount;
                }
                return ret;
            }
        }

        public int triangle
        {
            get
            {
                int ret = 0;
                if (mesh != null)
                {
                    ret = mesh.triangles.Length / 3;
                }
                return ret;
            }
        }

        public int triangles
        {
            get
            {
                int ret = triangle * refCount;
                return ret;
            }
        }

        public string GetColumn(MeshRefColumn index)
        {
            // throw new NotImplementedException();
            switch (index)
            {
                case MeshRefColumn.Fbx:
                    return mesh.name;
                case MeshRefColumn.Ref:
                    return refCount.ToString();
                case MeshRefColumn.Single:
                    return triangle.ToString();
                case MeshRefColumn.Total:
                    return triangles.ToString();
            }

            return (-1).ToString();
        }
    }

    public enum MeshRefColumn
    {
        Fbx,
        Ref,
        Single,
        Total,
    }
    private static readonly MeshRefColumn[] ColumnsToShow = new MeshRefColumn[]
        {
            MeshRefColumn.Fbx,
            MeshRefColumn.Ref,
            MeshRefColumn.Single,
            MeshRefColumn.Total,
        };

    private static readonly Dictionary<MeshRefColumn, string> ColumnHeaders = new Dictionary<MeshRefColumn, string>
        {
            { MeshRefColumn.Fbx, "网格"},
            { MeshRefColumn.Ref, "引用"},
            { MeshRefColumn.Single, "单个网格面数"},
            { MeshRefColumn.Total, "全部网格面数"},
        };
    public class MeshRefTableState : TableGUILayout.ITableState
    {
        private SplitterState splitterState;
        private IEnumerable<string> columnHeaders;

        public MeshRefTableState(MeshRefColumn[] columnsToShow, Dictionary<MeshRefColumn, string> columnHeaders)
        {
            this.columnHeaders = columnHeaders.Values;
            var splitterRelativeSizes = new float[columnsToShow.Length + 1];
            var splitterMinWidths = new int[columnsToShow.Length + 1];
            for (int i = 0; i < columnsToShow.Length; i++)
            {
                var column = columnHeaders[columnsToShow[i]];
                splitterMinWidths[i] = (int)GUI.skin.GetStyle("OL title").CalcSize(new GUIContent(column)).x;
                splitterRelativeSizes[i] = 70f;
            }
            splitterMinWidths[columnsToShow.Length] = 16;
            splitterRelativeSizes[columnsToShow.Length] = 0f;

            if (columnsToShow[0] == MeshRefColumn.Fbx)//special code！！！
            {
                splitterRelativeSizes[0] = 400f;
                splitterMinWidths[0] = 100;
            }
            splitterState = new SplitterState(splitterRelativeSizes, splitterMinWidths, null);
        }

        IEnumerable<string> TableGUILayout.ITableState.Headers
        {
            get
            {
                return columnHeaders;
            }
        }

        Vector2 TableGUILayout.ITableState.ScrollPosition
        {
            get;
            set;
        }

        SplitterState TableGUILayout.ITableState.SplitterState
        {
            get
            {
                return splitterState;
            }
        }
    }

    [Serializable]
    public class MeshRefData
    {
        private static readonly string[] ColumnNames = Enum.GetNames(typeof(MeshRefColumn));

        public string functionPath;
        public MeshRefDataValue[] values;

        public string GetValue(MeshRefColumn column)
        {
            var columnName = ColumnNames[(int)column];
            return FindDataValue(columnName).value;
        }

        private MeshRefDataValue FindDataValue(string columnName)
        {
            int length = values.Length;
            for (int i = 0; i < length; ++i)
            {
                var value = values[i];
                if (value.column == columnName)
                {
                    return value;
                }
            }
            return default(MeshRefDataValue);
        }

        public override string ToString()
        {
            return JsonUtility.ToJson(this);
        }

        public static MeshRefData Create(MeshRefInfo property)
        {
            var functionData = new MeshRefData();
            var columns = Enum.GetValues(typeof(MeshRefColumn));
            functionData.values = new MeshRefDataValue[columns.Length];
            for (int i = 0; i < columns.Length; ++i)
            {
                var column = (MeshRefColumn)columns.GetValue(i);
#if UNITY_5_5_OR_NEWER
                if (column == MeshRefColumn.DontSort)
                {
                    continue;
                }
#endif
                var functionDataValue = new MeshRefDataValue();
                functionDataValue.column = column.ToString();
                functionDataValue.value = property.GetColumn(column);
                functionData.values[i] = functionDataValue;
            }
            return functionData;
        }
    }

    [Serializable]
    public class MeshRefDataValue
    {
        public string column;
        public string value;
    }

    #endregion


    [UnityEditor.MenuItem("Slg/统计顶点数量(open window)")]
    static void Init()
    {
        ShowRenderVertexCount win = EditorWindow.GetWindow(typeof(ShowRenderVertexCount)) as ShowRenderVertexCount;
    }
    /// <summary>
	/// OnGUI is called for rendering and handling GUI events.
	/// This function can be called multiple times per frame (one call per event).
	/// </summary>
	void OnGUI()
    {
        if (GUILayout.Button("计算顶点数") == true)
        {
            Transform tran = Selection.activeTransform;
            CountMeshVertexNum(tran);
        }
        if (GUILayout.Button("清除名字后的顶点数") == true)
        {
            Transform tran = Selection.activeTransform;
            ClearMeshVertexNum(tran);
        }
        if (GUILayout.Button("BakeMesh") == true)
        {
            BakeNodeMesh();
            // Transform tran = Selection.activeTransform;
            // ClearMeshVertexNum(tran);
        }

        DrawMeshRef();
        // GUILayout.BeginHorizontal();
        // selectMesh = EditorGUILayout.ObjectField("Mesh", selectMesh, typeof(Mesh), false) as Mesh;
        // if (selectMesh == null) meshRefCount = 0;
        // if (GUILayout.Button("计算这个网格在场景中出现的总顶点数") == true)
        // {
        //     if (selectMesh != null)
        //         meshRefCount = GetMeshAllVertex(selectMesh);
        // }
        // GUILayout.Label(string.Format("Ref：{0}", meshRefCount));
        // GUILayout.EndHorizontal();
    }

    /// <summary>
    /// 绘制网格引用情况
    /// </summary>
    void DrawMeshRef()
    {

        if (GUILayout.Button("计算当前场景中网格的分布") == true)
        {
            meshRefList = GetAllMeshRefInfos();
            SortMeshRefInfoLst(MeshRefColumn.Total);
        }

        if (meshRefList.Count == 0)
        {
            return;
        }

        var stats = GetData();
        functionStats = stats.Select<MeshRefData, string[]>(f => ColumnsToShow.Select<MeshRefColumn, string>(f.GetValue).ToArray()).ToArray();

        if (functionStatsTableState == null)
        {
            functionStatsTableState = new MeshRefTableState(ColumnsToShow, ColumnHeaders);
        }

        if (functionStats != null)
        {
            TableGUILayout.SetClickCallback((string head) =>
            {
                foreach (var item in ColumnHeaders)
                {
                    if (item.Value == head)
                    {
                        SortMeshRefInfoLst(item.Key);
                    }
                }
            });
            TableGUILayout.BeginTable(functionStatsTableState, GUI.skin.GetStyle("OL Box"), GUILayout.MinHeight(100f), GUILayout.MaxHeight(500f));
            for (var i = 0; i < functionStats.Length; ++i)
            {
                var functionData = functionStats[i];
                TableGUILayout.AddRow(functionStatsTableState, i, functionData);
            }
            TableGUILayout.EndTable();
        }

        // curScrollPos = GUILayout.BeginScrollView(curScrollPos);
        // foreach (var item in meshRefList)
        // {
        //     GUILayout.BeginHorizontal();
        //     UnityEngine.Object obj = EditorGUILayout.ObjectField("Prefab", item.mesh, typeof(GameObject), false);

        //     GUILayout.Label(string.Format("Ref：{0}", item.refCount));
        //     GUILayout.Label(string.Format("Vertex：{0}", item.totalVertex));
        //     GUILayout.EndHorizontal();
        // }
        // GUILayout.EndScrollView();
    }

    /// <summary>
    /// 对数据排下序
    /// </summary>
    /// <param name="col"></param>
    void SortMeshRefInfoLst(MeshRefColumn col)
    {
        if (meshRefList == null)
        {
            Debug.LogWarning("");
            return;
        }
        Comparison<MeshRefInfo> comp = (x, y) =>
        {
            switch (col)
            {
                case MeshRefColumn.Fbx:
                    return y.mesh.name.CompareTo(x.mesh.name);
                case MeshRefColumn.Ref:
                    return y.refCount.CompareTo(x.refCount);
                case MeshRefColumn.Single:
                    return y.triangle.CompareTo(x.triangle);
                case MeshRefColumn.Total:
                    return y.triangles.CompareTo(x.triangles);
            }
            return y.triangles.CompareTo(x.triangles);
        };
        meshRefList.Sort(comp);
    }

    MeshRefData[] GetData()
    {
        List<MeshRefData> ret = new List<MeshRefData>();
        foreach (var item in meshRefList)
        {
            MeshRefData data = MeshRefData.Create(item);
            ret.Add(data);
        }
        return ret.ToArray();
    }

    /// <summary>
    /// 计算节点下的顶点数量并改名
    /// </summary>
    /// <param name="tran"></param>
    static void CountMeshVertexNum(Transform tran)
    {
        int count = 0;
        MeshFilter[] mfArr = tran.GetComponentsInChildren<MeshFilter>();
        foreach (var item in mfArr)
        {
            if (item.sharedMesh == null)
            {
                continue;
            }

            count += item.sharedMesh.vertexCount;
        }

        SkinnedMeshRenderer[] skmArr = tran.GetComponentsInChildren<SkinnedMeshRenderer>();
        foreach (var item in skmArr)
        {
            if (item.sharedMesh == null)
            {
                continue;
            }

            count += item.sharedMesh.vertexCount;
        }

        tran.name = string.Format("{0}{1}{2}", tran.name, CONSTANT_NAME, count);

        foreach (Transform item in tran)
        {
            CountMeshVertexNum(item);
        }
    }

    /// <summary>
    /// 清除节点上顶点数字
    /// </summary>
    /// <param name="tran"></param>
    static void ClearMeshVertexNum(Transform tran)
    {
        if (tran.name.Contains(CONSTANT_NAME) == true)
        {
            string[] separators = new string[] { CONSTANT_NAME };
            string[] strs = tran.name.Split(separators, StringSplitOptions.RemoveEmptyEntries);
            if (strs.Length > 0)
                tran.name = strs[0];
        }

        foreach (Transform item in tran)
        {
            ClearMeshVertexNum(item);
        }
    }

    static void BakeNodeMesh()
    {
        GameObject bakerObj = MB3_MeshBakerEditor.CreateNewMeshBaker();
        MB3_TextureBaker tb = bakerObj.GetComponent<MB3_TextureBaker>();
        MB3_MeshBaker mb = bakerObj.GetComponent<MB3_MeshBaker>();


        MB3_MeshBakerEditorWindow mmWin = (MB3_MeshBakerEditorWindow)EditorWindow.GetWindow(typeof(MB3_MeshBakerEditorWindow));
        mmWin.target = (MB3_MeshBakerRoot)tb;
        Type t = typeof(MB3_MeshBakerEditorWindow);
        FieldInfo fInfo = t.GetField("excludeMeshesWithOBuvs", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        fInfo.SetValue(mmWin, false);
        MethodInfo meInfo = t.GetMethod("addSelectedObjects", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        List<GameObject> objs = new List<GameObject>();
        meInfo.Invoke(mmWin, new object[] { objs.ToArray() });

        // var path = EditorUtility.SaveFilePanelInProject("Save texture as PNG",
        // "unsaved.png",
        // "png",
        // "Please enter a file name to save the texture to");
        // Debug.Log(path);
        if (tb.objsToMesh.Count > 0 && tb.objsToMesh[0] != null)
        {
            string str = "cangku";
            string path = string.Format("Assets/scene_tools/create_scene/create_world_scene/{0}", str);
            if (Directory.Exists(path) == false)
            {
                Directory.CreateDirectory(path);
            }
            DigitalOpus.MB.Core.MB3_TextureBakerEditorInternal.CreateCombinedMaterialAssets(tb, string.Format("{0}/{1}.asset", path, str));
            tb.CreateAtlases(DigitalOpus.MB.Core.MB3_TextureBakerEditorInternal.updateProgressBar, true, new DigitalOpus.MB.Core.MB3_EditorMethods());
            EditorUtility.ClearProgressBar();
            if (tb.textureBakeResults != null) EditorUtility.SetDirty(tb.textureBakeResults);
            GameObject obj = new GameObject(str);
            GameObject objPrefab = PrefabUtility.CreatePrefab(string.Format("{0}/{1}.prefab", path, str), obj);

            mb.meshCombiner.outputOption = DigitalOpus.MB.Core.MB2_OutputOptions.bakeIntoPrefab;
            mb.resultPrefab = objPrefab;
        }



    }

    /// <summary>
    /// 获取指定网格的引用情况
    /// </summary>
    /// <param name="ms"></param>
    /// <returns></returns>
    static int GetMeshAllVertex(Mesh ms)
    {
        int refCount = 0;
        GameObject[] objs = SceneManager.GetActiveScene().GetRootGameObjects();
        foreach (var tran in objs)
        {
            MeshFilter[] mfArr = tran.GetComponentsInChildren<MeshFilter>();
            foreach (var item in mfArr)
            {
                if (item.sharedMesh == null)
                {
                    continue;
                }

                if (item.sharedMesh.name == ms.name) refCount++;
            }

            SkinnedMeshRenderer[] skmArr = tran.GetComponentsInChildren<SkinnedMeshRenderer>();
            foreach (var item in skmArr)
            {
                if (item.sharedMesh == null)
                {
                    continue;
                }

                if (item.sharedMesh.name == ms.name) refCount++;
            }
        }

        return refCount;
    }

    /// <summary>
    /// 获取场景中所有网格的引用情况
    /// </summary>
    /// <returns></returns>
    static List<MeshRefInfo> GetAllMeshRefInfos()
    {
        List<MeshRefInfo> ret = new List<MeshRefInfo>();
        GameObject[] objs = SceneManager.GetActiveScene().GetRootGameObjects();
        foreach (var tran in objs)
        {
            MeshFilter[] mfArr = tran.GetComponentsInChildren<MeshFilter>();
            foreach (var item in mfArr)
            {
                if (item.sharedMesh == null)
                {
                    continue;
                }

                int index = IndexOf(ret, item.sharedMesh);
                if (index == -1)
                {
                    MeshRefInfo mri = new MeshRefInfo();
                    mri.mesh = item.sharedMesh;
                    mri.refCount = 1;
                    ret.Add(mri);
                }
                else
                {
                    ret[index].refCount++;
                }

            }

            SkinnedMeshRenderer[] skmArr = tran.GetComponentsInChildren<SkinnedMeshRenderer>();
            foreach (var item in skmArr)
            {
                if (item.sharedMesh == null)
                {
                    continue;
                }

                int index = IndexOf(ret, item.sharedMesh);
                if (index == -1)
                {
                    MeshRefInfo mri = new MeshRefInfo();
                    mri.mesh = item.sharedMesh;
                    mri.refCount = 1;
                    ret.Add(mri);
                }
                else
                {
                    ret[index].refCount++;
                }
            }
        }


        return ret;
    }



    /// <summary>
    /// 获取指定网格在引用信息列表中的索引
    /// </summary>
    /// <param name="lst"></param>
    /// <param name="mesh"></param>
    /// <returns></returns>
    static int IndexOf(List<MeshRefInfo> lst, Mesh mesh)
    {
        Predicate<MeshRefInfo> predicate = (item) =>
        {
            return item.mesh.GetInstanceID() == mesh.GetInstanceID();
        };

        return lst.FindIndex(predicate);
    }
}
